<template>
  <div :class="`column-container ${mode} ${isSelected ? 'selected' : ''}`">
    <div v-show="mode === 'panel'" class="edit-header">
      {{ configPanelTitle }}
      <span class="btn-done btn-cancel" @click="onCancel">取消</span>
      <span class="btn-done" @click="onOk">完成</span>
    </div>
    <div class="view-content">
      <div
        :draggable="dragEnabled"
        @dragstart="onDragstart($event)"
        @click="onDataColumnClick($event)"
      >
        <div class="top-icons">
          <!--  graph 原始表不允许 修改数据类型 -->
          <div
            v-if="changeDataTypeEnabled && !isVisitor"
            class="column-type-icon icon-container"
            @click.stop
          >
            <column-data-type-menu
              :source-type="column.desc"
              :semantic="column.semantic"
              :column="column"
              @click="onColumnDataTypeClick"
              @semantic-change="handleSemanticChange"
              @format-change="handleFormatChange"
            >
              <template #default="slotProps">
                <span class="column-type-icon-trigger">
                  <a-icon-font :type="columnTypeIcon[column.desc]" />
                  |
                  {{
                    !column.semantic || column.semantic === 'null'
                      ? '无'
                      : semanticMapping[column.semantic] | capitalize
                  }}
                  <a-icon
                    :class="{
                      arrow: true,
                      'menu-open': slotProps.data.menuVisible,
                    }"
                    type="caret-down"
                  />
                </span>
              </template>
            </column-data-type-menu>
          </div>
          <div class="btns icon-container" @click.stop>
            <a-icon-font
              v-show="!['date'].includes(column.desc)"
              class="icon"
              type="iconsousuo"
              @click="setSearchMode"
            />
            <column-sort-menu ref="columnSort" @click="sortColumn">
              <a-icon-font class="icon" type="iconpaixu" />
            </column-sort-menu>
            <column-menu
              v-if="!isVisitor"
              :column="column"
              :stat-mode="statMode"
              :tools="tools"
              :selected-columns="selectedColumns"
              @open-config-panel="toggleConfigPanel"
              @menu-item-clicked="handleMenuItemClicked"
              @formula-new-column="handleFormulaNewColumn"
              @create-new-column="handleCreateNewColumn"
              @partition-by="handlePartitionBy"
              @customize-menu-click="
                (key) => $emit('customize-menu-click', key)
              "
            >
              <a-icon-font class="icon" type="icongengduocaozuo" />
              <template slot="menu">
                <slot name="menu"></slot>
              </template>
            </column-menu>
          </div>
          <div v-if="isSelectedLabel" class="attr-icon icon-container">
            <a-icon-font class="icon" type="iconbiaoqian" />
          </div>
        </div>
        <div class="name-and-btns">
          <!--  graph 原始表不允许 重命名 -->
          <div class="column-name">
            <a-tooltip v-if="!isEditingColumnName" placement="top">
              <template slot="title">
                <span>{{ column.name }}</span>
              </template>
              <span
                :title="column.name"
                @dblclick="onDataColumnDBClick($event)"
                >{{ column.name }}</span
              >
            </a-tooltip>
            <!-- <span v-if="!isEditingColumnName" :title="column.name">{{ column.name }}</span> -->
            <!-- <input v-else :value="column.name" @keyup.enter="onColumnNameChange"/> -->
            <a-input
              v-else
              auto-focus
              size="small"
              style="height: 22px;"
              placeholder="不能包含#$%^等特殊字符"
              :max-length="30"
              :value="column.name"
              @click.stop
              @blur="onColumnNameChange($event)"
              @pressEnter="($event) => $event.target.blur()"
            />
          </div>
          <div v-if="chartConfig" class="unique-value-count">
            {{ columnStat.distinctTotal }}
          </div>
        </div>
      </div>
      <div class="divider"></div>
      <div class="stat-chart" :style="{ height: `${chartHeight}px` }">
        <div v-if="isSearchActive" class="search-box">
          <a-input
            v-model="searchValue"
            auto-focus
            allow-clear
            size="small"
            class="search-input"
            placeholder="按回车键发起搜索"
            @pressEnter="searchOptions"
            @change="onSearchChange"
          />
        </div>
        <!-- <a-input v-show="chartOptionsEdit" v-model="chartOptionsInputValue" sime="small" class="chart-options-input" :style="chartOptionsInputStyle" /> -->
        <column-statistics
          v-show="chartConfig"
          ref="columnStatistics"
          :width="160"
          :height="chartHeight"
          v-bind="chartConfig"
          :contextmenu-enable="contextmenuEnable"
          @bar-click="onBarClick"
          @bar-scroll="onBarScroll"
          @range-bar-click="onOverviewMove"
          @contextmenu="(event) => $emit('contextmenu', event)"
        />
        <div
          v-if="(!chartConfig && isLoading) || !visible"
          class="mask-bar-container"
        >
          <div
            v-for="(barLength, i) in maskBarLength"
            :key="i"
            :style="{ width: `${barLength}px` }"
            class="mask-bar"
          ></div>
        </div>
        <div
          v-else-if="!isLoading && !chartConfig && visible"
          style="margin-top: 24px; text-align: center;"
        >
          暂无数据
        </div>
      </div>
      <div
        v-show="showIndex && column.name !== currentCol"
        class="column-number"
      >
        {{ `C${orderNumber + 1}` }}
      </div>
    </div>
    <div v-if="mode !== 'view'" class="config-panel">
      <component
        :is="configPanelType"
        :total-rows="statTotal"
        :unique-rows="statUnique"
        :column-data="column"
        :request-params="componentParams"
        :column="column"
        :column-stat="columnStat"
        :table-name="tableName"
        :parent-table-name="parentTableName"
        :task-id="taskId"
        :is-edit="isEdit"
        :edit-data="editData"
        :selected-columns="selectedColumns"
        :chart-selected-data-array="chartSelectedDataArray"
        :aggre-type="aggreType"
        :column-list="columnList"
        :semantic-transformation-config-info="semanticTransformationConfigInfo"
        :project-id="projectId"
        @confirm="createNewColumn"
        @on-filter-params-change="onFilterParamsChange"
        @on-filter-params-reset="onFilterParamsReset"
        @config-cancel="onCancel"
        @confirm-order="confirmCancelOrder"
        @change-semantic="createNewSemanticColumn"
      >
      </component>
    </div>
    <slot name="footer"></slot>
    <!-- mask -->
    <div :class="[column.name === currentCol ? 'notAttribute' : '']"></div>
  </div>
</template>

<script lang="ts">
import { Vue, Component, Prop, Watch } from 'vue-property-decorator'
import Chart from '@/components/chart/chart.vue'
import {
  ColumnStat,
  EventMessage,
  IChangeSemanticInfo,
} from '@/components/studio/data/node-edit-panel/interface'
import { IColumn } from '@/store/modules/dataview'
import { apiGetColumnStat, apiValidateTransform } from '@/api/data-clean'
import ColumnMenu from '@/components/studio/data/node-edit-panel/ColumnMenu.vue'
import ColumnSortMenu from '@/components/studio/data/node-edit-panel/ColumnSortMenu.vue'
import ColumnDataTypeMenu from '@/components/studio/data/node-edit-panel/ColumnDataTypeMenu.vue'
import FilterSelectedValue from '@/components/studio/data/node-edit-panel/FilterSelectedValue.vue'
import FilterNullValue from '@/components/studio/data/node-edit-panel/FilterNullValue.vue'
import FilterWildcardMatch from '@/components/studio/data/node-edit-panel/FilterWildcardMatch.vue'
import PartitionBy from '@/components/studio/data/node-edit-panel/PartitionBy.vue'
import FilterNumberRange from '@/components/studio/data/node-edit-panel/FilterNumberRange.vue'
import FilterDateRange from '@/components/studio/data/node-edit-panel/FilterDateRange.vue'
import SplitColumn from '@/components/studio/data/node-edit-panel/SplitColumn.vue'
import MergeColumn from '@/components/studio/data/node-edit-panel/MergeColumn.vue'
import AggregateFunction from '@/components/studio/data/node-edit-panel/AggregateFunction.vue'
import ColumnStatistics from '@/components/studio/data/node-edit-panel/ColumnStatistics.vue'
import { flatten, isEqual, throttle, isArray, isBoolean } from 'lodash'
import { isCtrlSelect } from '@/util/util'
import { KeyValueStore } from '@/components/vueGridLayout/interfate/grid-Item-data-inteface'
import { StatisticsAction } from '@/util/data-selection-actions'
import CreateColumnView from '@/components/studio/data/node-edit-panel/CreateColumnView.vue'
import ChimeraIntoJson from '@/components/studio/data/node-edit-panel/ChimeraIntoJson.vue'
import JsonParse from '@/components/studio/data/node-edit-panel/JsonParse.vue'
import ChimeraIntoArray from '@/components/studio/data/node-edit-panel/ChimeraIntoArray.vue'
import ArrayParse from '@/components/studio/data/node-edit-panel/ArrayParse.vue'
import EditOrder from '@/components/studio/data/node-edit-panel/EditOrder.vue'
import SemanticTransitionCreateColumnView from '@/components/studio/data/node-edit-panel/SemanticTransitionCreateColumnView.vue'
import HandleNullOutliers from '@/components/studio/data/node-edit-panel/HandleNullOutliers.vue'
import HandleSpecificOutliers from '@/components/studio/data/node-edit-panel/HandleSpecificOutliers.vue'
import HandleWildcardOutliers from '@/components/studio/data/node-edit-panel/HandleWildcardOutliers.vue'

import { columnTypeIcon } from '@/components/studio/data/node-edit-panel/column-type-semantic-icon'
import {
  columnTypeMapping,
  semanticMapping,
} from '@/components/studio/data/node-edit-panel/column-type-semantic'
import Big from 'big.js'

interface ChartDataItem {
  label: string
  value: [number, number]
  index: number
  low?: string | number
  up?: string | number
}

interface ChartUpdateOptions {
  scroll?: 'up' | 'down'
  updateOverview?: boolean
  pageNumber?: number
}

interface IKeyValueStore {
  [key: string]: any
}

const FILTER_COMPONENT_MAPPING: IKeyValueStore = {
  [StatisticsAction.filterNumberRange]: {
    componentName: 'filter-number-range',
    text: '值范围筛选',
  },
  [StatisticsAction.filterDateRange]: {
    componentName: 'filter-date-range',
    text: '日期范围筛选',
  },
  [StatisticsAction.filterNull]: {
    componentName: 'filter-null-value',
    text: 'Null值筛选',
  },
  [StatisticsAction.filterWildcard]: {
    componentName: 'filter-wildcard-match',
    text: '通配符筛选',
  },
  [StatisticsAction.filterSelected]: {
    componentName: 'filter-selected-value',
    text: '特定值筛选',
  },
  [StatisticsAction.partitionBy]: {
    componentName: 'partition-by',
    text: '分组排序新增列',
  },
  [StatisticsAction.splitColumn]: {
    componentName: 'split-column',
    text: '拆分字段',
  },
  [StatisticsAction.mergeColumn]: {
    componentName: 'merge-column',
    text: '合并字段',
  },
  [StatisticsAction.createColumn]: {
    componentName: 'create-column-view',
    text: '创建新列',
  },
  [StatisticsAction.aggreFunc]: {
    componentName: 'aggregate-function',
    text: '聚合函数',
  },
  [StatisticsAction.chimeraIntoJson]: {
    componentName: 'chimera-into-json',
    text: '嵌合成对象',
  },
  [StatisticsAction.jsonParse]: {
    componentName: 'json-parse',
    text: '对象解析',
  },
  [StatisticsAction.chimeraIntoArray]: {
    componentName: 'chimera-into-array',
    text: '嵌合成数组',
  },
  [StatisticsAction.arrayParse]: {
    componentName: 'array-parse',
    text: '数组解析',
  },
  [StatisticsAction.editOrder]: {
    componentName: 'edit-order',
    text: '编辑顺序',
  },
  [StatisticsAction.semanticTransitionCreateColumn]: {
    componentName: 'semantic-transition-create-column-view',
    text: '修改语义新增列',
  },
  [StatisticsAction.handleSpecificOutliers]: {
    componentName: 'handle-specific-outliers',
    text: '异常值处理-特定值',
  },
  [StatisticsAction.handleWildcardOutliers]: {
    componentName: 'handle-wildcard-outliers',
    text: '异常值处理-通配符匹配',
  },
  [StatisticsAction.handleNullOutliers]: {
    componentName: 'handle-null-outliers',
    text: '异常值处理-Null值',
  },
}

// 列名称中不可用字符
const SPECIAL_CHARACTER_REPLACE =
  '[`~!@#$%^&*()+=|{}\':;\',[].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。，、？"]'

@Component({
  components: {
    Chart,
    ColumnMenu,
    ColumnSortMenu,
    ColumnDataTypeMenu,
    FilterSelectedValue,
    FilterNullValue,
    FilterWildcardMatch,
    PartitionBy,
    FilterNumberRange,
    FilterDateRange,
    SplitColumn,
    MergeColumn,
    ColumnStatistics,
    CreateColumnView,
    AggregateFunction,
    ChimeraIntoJson,
    JsonParse,
    ChimeraIntoArray,
    ArrayParse,
    EditOrder,
    SemanticTransitionCreateColumnView,
    HandleSpecificOutliers,
    HandleNullOutliers,
    HandleWildcardOutliers,
  },
  filters: {
    capitalize(value: string) {
      if (value.length > 4) {
        return `${value.slice(0, 4)}...`
      }
      return value
    },
  },
})
export default class DataColumn extends Vue {
  @Prop() private column!: IColumn
  @Prop({ default: false }) private isEdit!: boolean // 是否编辑模式
  @Prop({ default: () => ({ data: {} }) }) editData!: KeyValueStore // 编辑模式下回显的数据
  @Prop() tableName!: string // 查询数据需要的接口参数
  @Prop({ default: [] }) columnFilters!: any[] // 查询数据需要的接口参数，用于筛选数据
  @Prop({ default: [] }) columnList!: IColumn[] // 全部的列字段
  @Prop({ default: 0 }) columnIndex!: number // 列索引
  @Prop({ default: [] }) selectedColumns!: IColumn[] // 用户选中的列字段
  @Prop() taskId!: string // selectnode id 等用于查询的特定id，查询数据需要的接口参数，当tableName是表全名时，该项可以不传
  @Prop({ default: false }) dragEnabled!: boolean // 是否禁用拖拽，默认禁用
  // graph 原始表不允许 修改数据类型和语义 重命名
  @Prop({ default: true }) changeDataTypeEnabled!: boolean // 是否禁用修改类型和语义, 默认禁用
  @Prop({ default: true }) renameEnabled!: boolean // 是否禁用修改name, 默认禁用
  @Prop({ default: '' }) parentTableName!: string // 编辑状态下是基于上一条记录的表名来进行筛选
  @Prop({ default: false }) contextmenuEnable!: boolean // 是否开启统计区域右键功能
  @Prop({ default: [] }) tools!: string[] // 启用的菜单功能列表，为null关闭全部功能，空数组启用全部功能
  @Prop({ default: false }) isSelectedLabel!: boolean // 是否为选中的标签
  @Prop({ default: false }) visible!: boolean // 是否在可视区域可见
  @Prop({ default: () => ({ data: {} }) }) cache!: KeyValueStore // 缓存
  @Prop({ default: false }) public isVisitor!: boolean // 是否访客模式
  @Prop({ default: 0 }) public orderNumber!: Number // 数据公式列序号
  @Prop({ default: false }) public showIndex?: Boolean // 是否显示数据公式列序号
  @Prop({ default: '' }) public currentCol?: String // 编辑公式状态下 公式列名
  @Prop() projectId!: number

  private createNewColumnFilter: any = null

  private get statName() {
    return this.columnStat?.name ?? ''
  }

  private get statTotal() {
    return this.columnStat?.total ?? 0
  }

  private get statUnique() {
    return this.columnStat?.distinctTotal ?? 0
  }

  //  点击确认之后的操作
  private createNewColumn(data: any) {
    this.createNewColumnFilter = data
    this.handleMenuItemClicked(StatisticsAction.createColumn)
  }

  private chartConfig: KeyValueStore | null = null
  private columnStat: ColumnStat | null = null
  private chartHeight: number = 194
  private columnTypeIcon = columnTypeIcon // 统一数据类型 icon
  private semanticMapping = semanticMapping // 语义中文文案
  private maskBarLength: number[] = [112, 81, 81, 81, 38, 57, 75, 112]
  private visibleBarNumber = 8 // cjh TODO
  private overviewBarNumber = 60 // cjh TODO
  private mode: 'view' | 'dialog' | 'panel' = 'view' // view：详情态，dialog：dialog模式，panel：面板模式
  private configPanelType = 'filter-selected-value'
  private configPanelTitle = ''
  private aggreType = 'sum' //  聚合函数类型
  private filterParams: any = null // 清洗操作面板抛出的清洗参数
  private chartSelectedDataArray: ChartDataItem[] = [] // bar点击选中的选项
  private isSorting = false
  private isSearchActive: boolean = false
  private isEditingColumnName: boolean = false
  private userSetStatMode: string = ''
  private searchValue = '' // 搜索输入值
  private searchFilter: any[] = []
  // private chartOptionsInputValue = '' // chart中unique值修改
  // private chartOptionsEdit = false
  // private chartOptionsInputStyle: any = {}
  private isLoading: boolean = false // 数据是否加载中

  private requestParams = {
    curPage: 1,
    pageSize: 50,
    sortType: 1,
    sortVal: 'ASC',
  }
  private oldBarScrollPayload = null
  private oldOverviewSelectedIndex = null

  /**
   * 语义类型转换
   */
  private async handleSemanticChange(semantic: any) {
    if (this.isVisitor) {
      this.$message.error('暂无操作权限')
      return
    }
    this.toSemantic = semantic
    // 进行语义转化验证
    const columnResult = await this.semanticTransformationValidation(semantic)

    // false 完全不能转换
    // array 空数组完全能转换
    // array 数组部分不能转换
    if (isBoolean(columnResult) && !columnResult) {
      this.$message.error('语义转换验证不通过, 不允许转换！')
      // 可转
    } else if (isArray(columnResult) && columnResult.length === 0) {
      // 如果是 有序
      if (semantic === 'order') {
        // 打开编辑 dialog 编辑有序顺序
        this.openSpecifiedDialog(StatisticsAction.editOrder)
        return
      }
      this.confirmChangeSemantic(semantic, this.CreateNewColumnName(semantic))
      // 大于0， 有不符合的行
    } else if (isArray(columnResult) && columnResult.length > 0) {
      // 创建新列的信息
      const createColumnInfo: IChangeSemanticInfo = {
        value: columnResult,
        toSemantic: semantic,
      }
      this.semanticTransitionCreateColumn(createColumnInfo)
    }
  }

  /**
   * 语义验证转换
   */
  public async semanticTransformationValidation(toSemantic: any) {
    const parameters = {
      col: this.column.name,
      type: this.column.desc,
      semantic: this.column.semantic,
      toSemantic,
      table: this.tableName,
    }
    const response = await apiValidateTransform({ data: parameters })
    // 列检查结果  any[] | boolean
    return response.data.result[this.column.name]
  }

  /**
   * 语义转换  emit DataColumns.vue 请求接口
   * @param toSemantic 转换的语义
   * @param newCol 新列明
   * @param other { order? 有序 顺序, update: 含有不符合规则的语义 修改}
   */
  public confirmChangeSemantic(
    toSemantic: string,
    newCol: string,
    other?: any
  ) {
    let data: any = {
      action: 'SEMANTIC_TRANSFORM',
      col: this.column.name,
      semantic: this.column.semantic,
      toSemantic,
      description:
        toSemantic === 'null'
          ? '转换为无语义'
          : `新增语义为${(semanticMapping as any)[toSemantic]}的列:${newCol}`,
      actionType: StatisticsAction.semanticTransform,
    }
    // order 有序语义转换
    if (other && Object.keys(other).includes('order')) {
      data = { order: other.order, ...data }
    }
    // 含有 update 含有不符合规则的语义 修改信息
    if (other && Object.keys(other).includes('update')) {
      data = { update: other.update, ...data }
    }
    // 不转换空语义新增列（列名）
    if (toSemantic !== 'null') {
      data = {
        columnName: newCol,
        newCol,
        col: newCol, // 与之前开发的一致， col newCol 都传参
        ...data,
      }
    }
    this.$emit('semantic-change', data)
  }

  /**
   * 创建新列名
   */
  private CreateNewColumnName(semantic: string) {
    const tableColumns = this.columnList
    const newColumnName: string = `${this.column.name}_${semantic}`

    //  防止重名；
    let index = 1
    let i = 0
    const { length } = tableColumns
    while (i < length) {
      if (tableColumns[i].name === `${this.column.name}_${index}`) {
        index += 1
      } else {
        i += 1
      }
    }

    return index > 0 ? `${newColumnName}_${index}` : newColumnName
  }

  private toSemantic: string | null | undefined = null // 将要变换的语义

  /**
   * 修改语义成功后修改该列语义刷新数据
   */
  public semanticChangeSuccess() {
    //  更改语义之后，需要重新获取该列的queryStat
    if (this.toSemantic) {
      this.column.semantic = this.toSemantic
      this.refreshColumnStat()
      this.$emit('closeRecomPart')
    }
  }

  private onDragstart(event: DragEvent) {
    console.log('dragstart_handler')
    let dragColumnName = document.querySelector('#dragColumn') as HTMLElement
    if (!dragColumnName) {
      dragColumnName = document.createElement('div')
      dragColumnName.id = 'dragColumn'
      dragColumnName.style.position = 'fixed'
      dragColumnName.style.borderRadius = '30px'
      dragColumnName.style.padding = '10px 5px'
      dragColumnName.style.textAlign = 'center'
      dragColumnName.style.color = 'white'
      dragColumnName.style.backgroundColor = '#a4abfb'

      document.body.append(dragColumnName)
    }

    dragColumnName.innerHTML = this.column.name
    event.dataTransfer?.setDragImage(dragColumnName, 90, 60) // 设置拖放时图像 ToDo:设置拖放的必需数据
    const nodeData = `${this.column.name}:${this.tableName}:${this.column.type}`
    event.dataTransfer?.setData('text/plain', nodeData)
  }

  private get statMode(): string {
    if (!this.column || !this.column.desc) {
      return ''
    }
    return this.mode !== 'view' || this.isSorting
      ? 'detail'
      : this.userSetStatMode
      ? this.userSetStatMode
      : ['varchar', 'json', 'array', 'date'].includes(this.column.desc) // 2021.08.21 时间查看 详情 增加 date
      ? 'detail'
      : 'binned'
  }

  private get isSelected() {
    return (
      this.selectedColumns.findIndex((c) => c.name === this.column.name) > -1
    )
  }

  private get componentParams() {
    return {
      ...this.requestParams,
      taskId: this.taskId,
      table: this.tableName,
      name: this.column.name,
      type: this.column.type,
      mode: 1, // 详情
      projectId: this.projectId,
    }
  }

  /**
   * 查询字段统计信息的基本参数
   */
  private get columnStatParams() {
    const data: any = {
      ...this.requestParams,
      table: this.tableName,
      name: this.column.name,
      type: this.column.type,
      mode: this.statMode === 'detail' ? 1 : 2,
      filter: this.columnFilters,
      taskId: this.taskId,
    }
    if (this.isSearchActive) {
      data.searchFilter = this.searchFilter
    }
    return data
  }

  /**
   * 查询字段统计信息的额外参数，用于后端减少查询开销
   * 上次响应中的几个字段，仅依赖taskId，table，column
   */
  private get stableColumnStatParams() {
    if (!this.columnStat) {
      return null
    }
    const { total, distinctTotal, topColumn, max, min } = this.columnStat
    return { total, distinctTotal, topColumn, max, min }
  }

  private get columnIdentity(): string {
    return `${this.column.name}-${this.column._stamp}` // 每次重新获取table数据后列头里_stamp会重新赋值，这里便于监听变化
  }

  @Watch('columnIdentity', { immediate: true })
  private async onColumnChange(newValue: string, oldValue: string) {
    if (newValue !== oldValue) {
      this.resetParams()
    }
    if (
      !this.tableName ||
      (!this.taskId && this.tableName.endsWith('_')) ||
      !this.visible
    ) {
      // 表名用下划线结尾证明是不完整表名，需要taskId辅助查询
      return
    }
    this.chartConfig = null
    this.mode = 'view'
    this.columnStat = await this.getNewColumnStat()
    this.cache[this.columnIdentity] = this.columnStat
    this.chartConfig = this.getChartConfig(this.columnStat) // 利用v-if条件chartConfig，来诡异地强制chart重新渲染
    if (this.isEdit) {
      // 判断是否是历史编辑状态
      this.editHistory(this.editData)
    }
  }

  @Watch('visible')
  private async onVisibleChange(newValue: boolean) {
    if (!newValue) {
      this.columnStat = null
      this.chartConfig = null
      this.filterParams = null
      this.chartSelectedDataArray = []
      this.toggleConfigPanel('view')
      return
    }
    if (this.cache[this.columnIdentity]) {
      // 有缓存从缓存里取，否则重新获取数据
      this.columnStat = this.cache[this.columnIdentity]
    } else {
      // 否则重新获取数据并存入缓存
      this.columnStat = await this.updateColumnStat()
      this.cache[this.columnIdentity] = this.columnStat
    }
    this.chartConfig = this.getChartConfig(this.columnStat)
    if (this.isEdit && this.mode === 'view') {
      // 判断是否是历史编辑状态
      this.editHistory(this.editData)
    } else {
      this.filterParams = null
      this.chartSelectedDataArray = []
      this.toggleConfigPanel('view')
    }
  }

  @Watch('columnFilters', { deep: true })
  private async onColumnFiltersChange() {
    this.cache[this.columnIdentity] = null // 清空缓存
    if (
      (this.columnFilters.length === 0 ||
        this.columnFilters[0][0].col !== this.column.name) &&
      this.$refs.columnStatistics
    ) {
      ;(this.$refs.columnStatistics as any).reset()
    }
    if (!this.visible) {
      return
    }

    if (this.statMode === 'binned') {
      this.columnStat = await this.updateColumnStat()
      this.chartConfig = this.getChartConfig(this.columnStat)
    } else {
      // const chart = (<Chart>this.$refs.chart).compInstance
      const chart = this.$refs.columnStatistics as Vue
      // TODO
      const startBarIndex: number | undefined = chart.$props.value[0]?.index
      if (startBarIndex !== undefined) {
        const value = await this.getChartDataByIndex(startBarIndex)
        const overviewData = this.rangeCountToChartOverview(
          this.columnStat?.rangeCount ?? []
        )
        if (value) {
          this.updateChartData(value)
        }
        this.updateChartData(overviewData, { updateOverview: true })
      }
    }
  }

  @Watch('mode')
  onComponentModeChange(newValue: string) {
    if (newValue === 'panel') {
      // 面板模式
      this.$emit('open-config-panel')
    }
  }

  private enableMergeColumn() {
    this.mode = 'dialog'
    const data = FILTER_COMPONENT_MAPPING[StatisticsAction.mergeColumn]
    this.configPanelType = data.componentName
    this.configPanelTitle = data.text

    this.filterParams = null
  }

  public editHistory(historyData: KeyValueStore) {
    if (!historyData) {
      return
    }
    // const history = this.currentHistory.data
    const history = historyData.data
    if (history.col === this.column.name) {
      this.mode = [
        StatisticsAction.splitColumn,
        StatisticsAction.mergeColumn,
        StatisticsAction.chimeraIntoArray, // 嵌合成对象是 dialog
        StatisticsAction.chimeraIntoJson, // 嵌合成数组是 dialog
        StatisticsAction.editOrder, // 编辑有序顺序是 dialog
        StatisticsAction.partitionBy, // rownumber是 dialog
      ].includes(history.actionType)
        ? 'dialog'
        : 'panel'
      this.configPanelType =
        FILTER_COMPONENT_MAPPING[history.actionType].componentName // 设置编辑面板
      this.configPanelTitle = FILTER_COMPONENT_MAPPING[history.actionType].text
      //  this.refreshColumnStat()
    } else if (
      history.newCol === this.column.name &&
      history.actionType === StatisticsAction.renameColumn
    ) {
      this.isEditingColumnName = true
    } else {
      this.filterParams = null
      this.chartSelectedDataArray = []
      this.toggleConfigPanel('view')
    }
  }

  private updateColumnStat(): Promise<ColumnStat | null> {
    const parameters: any = this.columnStatParams
    if (this.stableColumnStatParams) {
      parameters.data = this.stableColumnStatParams
    }
    return this.getColumnStat(parameters)
  }

  private getNewColumnStat(): Promise<ColumnStat | null> {
    return this.getColumnStat(this.columnStatParams)
  }

  private async getColumnStat(data: any): Promise<ColumnStat | null> {
    if (!this.column) {
      return null
    }
    //  添加某一列的语义信息
    data.data = {
      semantic: this.column.semantic,
    }
    this.isLoading = true
    const response = await apiGetColumnStat({ data })
    this.isLoading = false
    if (response.status === 200 && response.data.success) {
      return response.data.result
    }
    return null
  }

  private getChartConfig(columnStat: ColumnStat | null) {
    if (!columnStat) {
      return null
    }

    let mode: string
    let maxValue: number
    let value: { label: any; value: number[] }[]
    let overviewOptions: number[][]
    let barRatio: number = 1

    if (this.statMode === 'detail') {
      mode = 'discrete'
      maxValue = columnStat.topColumn.count
      value = this.pageCountToChartValue(columnStat.pageCount) || []
      overviewOptions = this.rangeCountToChartOverview(columnStat.rangeCount)
      let rangeCountElement: any = {}
      try {
        rangeCountElement =
          (columnStat.rangeCount[0].low === null
            ? columnStat.rangeCount[1]
            : columnStat.rangeCount[0]) || {}
      } catch {
        rangeCountElement = {}
      }
      barRatio = !Number.isNaN(rangeCountElement.up - rangeCountElement?.low)
        ? rangeCountElement.up - rangeCountElement?.low
        : 1
    } else {
      mode = 'continuous'
      maxValue = Math.max(...columnStat.rangeCount.map((d) => d.count))
      value = columnStat.rangeCount.map((d, i) => ({
        label: d.low === null ? 'null' : `${d.low} - ${d.up}`,
        index: i,
        yAxisValue: d.low,
        // 增加 异常高亮， 插补高亮
        value: [d.count, d.highLight || 0, d.anomaly || 0, d.imputation || 0],
        low: d.low,
        up: d.up,
      }))
      overviewOptions = []
    }

    return {
      semantic: this.column.semantic,
      maxValue,
      totalValue: columnStat.distinctTotal,
      mode,
      radio: barRatio,
      value,
      totalCount: columnStat.total,
      overviewOptions,
      barHeight: mode === 'continuous' ? 10 : 20,
      barGap: mode === 'continuous' ? 0 : 1,
      tooltipIsShow: true,
      barColor: '#E9E9E9',
      subBarColor: '#B6BAF1',
      overviewFocusBarStrokeColor: '#D8D8D8',
    }
  }

  private async toggleConfigPanel(
    flag: 'dialog' | 'panel' | 'view',
    type?: string,
    aggreType?: string
  ) {
    this.mode = flag
    if (flag !== 'view' && type) {
      this.configPanelType = FILTER_COMPONENT_MAPPING[type].componentName
      this.configPanelTitle = FILTER_COMPONENT_MAPPING[type].text
      this.filterParams = null
      if (flag === 'panel') {
        this.refreshColumnStat()
      }
      if (aggreType) {
        //  如果存在aggreType传参数
        this.aggreType = aggreType
      }
    }
  }

  private async refreshColumnStat() {
    this.chartConfig = null
    this.columnStat = await this.updateColumnStat()
    this.$nextTick(() => {
      this.chartConfig = this.getChartConfig(this.columnStat)
    })
  }

  /**
   * 接收筛选参数
   */
  private onFilterParamsChange(data: any) {
    this.filterParams = data
    if (this.mode === 'dialog') {
      // dialog 模式下直接触发清洗
      this.onOk()
    }
  }

  /**
   * 清空筛选参数
   */
  private onFilterParamsReset() {
    this.filterParams = null
  }

  /**
   * 清洗操作操作完成：新加、编辑操作历史
   */
  private async onOk() {
    this.toggleConfigPanel('view')
    if (!this.filterParams) {
      this.$emit('config-success', null)
      return
    }
    const data = {
      ...this.filterParams.data,
    }
    this.$emit('config-success', data)
  }

  /**
   * 清洗面板取消操作
   */
  private onCancel() {
    this.filterParams = null
    this.chartSelectedDataArray = []
    this.toggleConfigPanel('view')
    this.$emit('config-cancel')
  }

  private get columnNames(): string[] {
    return this.columnList.map((c) => c.name)
  }

  /**
   * 重命名字段
   */
  private async onColumnNameChange(event: KeyboardEvent) {
    let name = (<HTMLInputElement>event?.target)?.value.replace(/\s+/g, '')
    this.$emit('config-cancel')

    SPECIAL_CHARACTER_REPLACE.split('').forEach((char) => {
      // 去除特殊字符
      name = name.replaceAll(char, '')
    })

    if (name === this.column.name.trim()) {
      this.isEditingColumnName = false
      return
    }
    if (name && !this.columnNames.includes(name)) {
      this.filterParams = {
        data: {
          action: 'RENAME',
          col: this.isEdit ? this.editData?.data.col : this.column.name,
          newCol: name,
          description: `重命名为${name}`,
          actionType: StatisticsAction.renameColumn, // 这里之前是rename'rename',
        },
      }
      this.onOk()
    } else {
      this.$message.info(
        name ? `字段名为:${name}，和已有字段名重复` : '字段名不能为空'
      )
    }
    this.isEditingColumnName = false
  }

  /**
   * 处理barclick 多选情况
   * 默认按住ctrl或者shift才会走到这个函数
   * @param appendDataArray 追加的字段
   * @param isCtrl 是否按住ctrl键
   */
  private parseMultipleBarClick(
    appendDataArray: ChartDataItem[],
    isCtrl: boolean
  ) {
    const { chartSelectedDataArray } = this
    if (appendDataArray.length > 1) {
      // 大于1个证明是brush框选模式，直接用追加的字段
      return appendDataArray
    }
    if (isCtrl) {
      // ctrl多选模式
      if (
        chartSelectedDataArray.findIndex(
          (item) => item.label === appendDataArray[0].label
        ) > -1
      ) {
        // 已存在情况取消选中
        return chartSelectedDataArray.filter(
          (item) => item.label !== appendDataArray[0].label
        )
      }
      return [
        // 添加选中项
        ...chartSelectedDataArray,
        ...appendDataArray,
      ]
    }
    // 按住shift键情况下
    const startIndex = Math.min(
      chartSelectedDataArray.slice(-1)[0].index,
      appendDataArray[0].index
    )
    const endIndex = Math.max(
      chartSelectedDataArray.slice(-1)[0].index,
      appendDataArray[0].index
    )

    if (startIndex === endIndex) {
      return chartSelectedDataArray
    }
    return [
      ...chartSelectedDataArray.filter(
        (item) => ![startIndex, endIndex].includes(item.index)
      ),
      ...this.chartConfig?.value.slice(startIndex, endIndex + 1),
    ]
  }

  private onBarClick({ selectedDataArray }: any, event: MouseEvent) {
    const filters: any[] = []
    let data = [...selectedDataArray]

    if (selectedDataArray.length === 0) {
      this.$emit('bar-click', [])
      return
    }

    // 仅在单个bar 被点击的时候触发
    const parameters = {
      semantic: this.column.semantic,
      selectValue: data[0].label,
    }
    if (
      (event && (isCtrlSelect(event) || event.shiftKey)) ||
      data[0].isMatching ||
      !(this.$refs.columnStatistics as any).urbanSemanticList.includes(
        this.column.semantic
      )
    ) {
      this.$emit('closeRecomPart')
    } else {
      this.$emit('openVRPanel', parameters)
    }

    // 之前有filter字段并且是当前列下的filter才会判断是不是多选，非当前列下filter直接使用选中的bar
    if (
      this.columnFilters.length > 0 &&
      this.columnFilters[0][0].col === this.column.name &&
      event &&
      (isCtrlSelect(event) || event.shiftKey) &&
      this.configPanelType !== 'filter-field-group'
    ) {
      // 多选模式下 (值分组不可用)
      data = this.parseMultipleBarClick(selectedDataArray, isCtrlSelect(event))
    }
    this.chartSelectedDataArray = data

    if (this.statMode === 'binned') {
      const result: any[] = []
      data.forEach((d: ChartDataItem) => {
        if (d.low === null) {
          result.push({
            col: this.column.name,
            values: ['#NULL'],
            filterType: '=',
          })
        } else {
          result.push({
            col: this.column.name,
            values: [`${d.low}`, `${d.up}`], // 范围区间方式
            filterType: '[a,b)',
          })
        }
      })
      filters.push(result)
    } else {
      filters.push([
        {
          col: this.column.name,
          values: data.map((d: ChartDataItem) => {
            return d.label === null ? '#NULL' : `${d.label}`
          }),
          filterType: '=',
        },
      ])
    }
    this.$emit('contextmenu', null)
    this.$emit('bar-click', filters)
  }

  /**
   * throttled _onOverviewMove
   */
  private onOverviewMove = throttle((data) => this._onOverviewMove(data), 300)

  private async _onOverviewMove({ selectedRangeIndex }: any = {}) {
    if (this.oldOverviewSelectedIndex === selectedRangeIndex) {
      return
    }
    this.oldOverviewSelectedIndex = selectedRangeIndex
    // if (isEqual(this.oldOverviewMovePayload, selectedOverviewIndex)) return
    // this.oldOverviewMovePayload = selectedOverviewIndex

    // if (selectedOverviewIndex.length === 0) return // TODO: error param
    const distinctTotal = this.columnStat?.distinctTotal
    const rangeCountElement =
      this.columnStat?.rangeCount[0].low === null
        ? this.columnStat?.rangeCount[1]
        : this.columnStat?.rangeCount[0]!
    const barRatio = rangeCountElement.up - rangeCountElement?.low
    const startBarIndex = Math.min(
      Math.round(selectedRangeIndex) * barRatio,
      distinctTotal! - this.visibleBarNumber
    )
    const value = await this.getChartDataByIndex(startBarIndex)
    if (!value) {
      return
    }
    ;(this.chartConfig as any).value = value
    this.updateChartData(value)
    this.$nextTick(() => {
      ;(this.$refs.columnStatistics as any).scrollToTop(0)
    })
  }

  private async onBarScroll(payload: any) {
    if (isEqual(this.oldBarScrollPayload, payload)) {
      return
    }
    this.oldBarScrollPayload = payload

    const { deltaY, currentDataStartIndex, currentDataEndIndex } = payload
    const { pageSize } = this.requestParams
    const distinctTotal = this.columnStat?.distinctTotal
    const pageNumber = Math.ceil(distinctTotal! / pageSize)
    const nextPage =
      currentDataEndIndex + 1 < distinctTotal!
        ? Math.ceil((currentDataEndIndex + 2) / pageSize)
        : null
    const previousPage = Math.ceil(currentDataStartIndex / pageSize)

    let newPage
    if (deltaY > 0 && nextPage && nextPage <= pageNumber) {
      newPage = nextPage
    } else if (deltaY < 0 && previousPage >= 1) {
      newPage = previousPage
    } else {
      return
    }

    const value = await this.getDetailDataPage(newPage)
    if (!value) {
      return
    }

    const valueSlice = value.slice(
      deltaY > 0 ? currentDataEndIndex + 1 - pageSize * (newPage - 1) : 0,
      deltaY > 0
        ? value.length
        : currentDataStartIndex - pageSize * (newPage - 1)
    )

    this.updateChartData(valueSlice, { scroll: deltaY > 0 ? 'down' : 'up' })
  }

  private async getChartDataByIndex(startBarIndex: number) {
    const { pageSize } = this.requestParams
    const startPage = Math.ceil((startBarIndex + 1) / pageSize)

    const endPage = Math.min(
      Math.ceil((startBarIndex + this.visibleBarNumber) / pageSize),
      this.columnStat?.pageCount?.totalPages!
    )
    if (endPage - startPage + 1 <= 0) {
      return null
    }
    const requests = new Array(endPage - startPage + 1)
      .fill(null)
      .map((_, i) => {
        this.requestParams.curPage = startPage + i
        return this.updateColumnStat()
      })
    const responses = await Promise.all(requests)
    this.columnStat = responses[responses.length - 1]

    return flatten(
      responses.map((response) =>
        this.pageCountToChartValue(response!.pageCount)
      )
    ).slice(startBarIndex - pageSize * (startPage - 1))
  }

  private async getDetailDataPage(page: number): Promise<any[] | null> {
    let pageCount: ColumnStat['pageCount'] | null

    if (page === this.columnStat?.pageCount?.curPage) {
      pageCount = this.columnStat.pageCount // cache
    } else if (page === this.requestParams.curPage) {
      pageCount = null // 请求已发未回
    } else {
      this.requestParams.curPage = page
      const columnStat = await this.updateColumnStat()
      if (page !== columnStat?.pageCount?.curPage) {
        pageCount = null // 已有更加新的请求返回
      } else {
        pageCount = columnStat?.pageCount
        this.columnStat = columnStat
      }
    }

    if (!pageCount) {
      return null
    }
    return this.pageCountToChartValue(pageCount)
  }

  // 数字格式化
  private getFormatValue(value: any, type: string, digit: number) {
    if (typeof value === 'number') {
      if (type === '%') {
        // 百分比计数法
        value = `${Big(value).times(100).toFixed(digit)}%`
      } else if (type === 'E-NOTATION') {
        // 科学计数法
        value = `${Big(value).toExponential(digit)}`
      }
    }
    return value
  }

  private pageCountToChartValue(pageCount: ColumnStat['pageCount']) {
    const { data, curPage, pageSize } = pageCount || {}
    if (!data) {
      return null
    }
    return data.map((d, i) => {
      let label = d.column

      if (this.columnList[this.columnIndex]) {
        const { numberFormat } = this.columnList[this.columnIndex]
        if (numberFormat) {
          const { action, digit } = numberFormat
          label = this.getFormatValue(label, action, digit)
        }
      }

      return {
        label,
        index: pageSize! * (curPage! - 1) + i,
        yAxisValue: d.column,
        value: [d.count, d.highLight || 0, d.anomaly || 0, d.imputation || 0],
        isMatching: d?.isMatching ?? true, //  当前值是否匹配列的语义
      }
    })
  }

  private rangeCountToChartOverview(rangeCount: ColumnStat['rangeCount']) {
    return rangeCount.map((d) => [d.count, d.highLight || 0])
  }

  private updateChartData(value: any[], options?: ChartUpdateOptions) {
    // const chart = (<Chart>this.$refs.chart)?.compInstance
    const chart = this.$refs.columnStatistics
    if (!chart) {
      return
    }

    let message: EventMessage
    if (options?.scroll) {
      message = new EventMessage(
        { detailData: value, dataPosition: options.scroll },
        'updatePartData',
        chart
      )
    } else if (options?.updateOverview) {
      message = new EventMessage(
        { overviewData: value },
        'updateOverviewData',
        chart
      )
    } else {
      message = new EventMessage({ data: value }, 'updateAllData', chart)
    }
    ;(chart as any).reloadData(message)
    // chart.$emit('part-render-event', message)
  }

  private async sortColumn({ by, order }: any) {
    this.isSorting = true
    this.requestParams = {
      ...this.requestParams,
      sortType: by === 'count' ? 2 : 1,
      sortVal: order,
      curPage: 1,
    }

    this.refreshColumnStat()
  }

  /**
   * 设置搜索框是否启用
   */
  private setSearchMode() {
    this.isSearchActive = !this.isSearchActive
    this.oldBarScrollPayload = null
    if (!this.isSearchActive && this.searchValue.trim()) {
      // 关闭搜索并且之前搜索值不为空时，需要重置
      this.searchValue = ''
      this.searchOptions()
      this.$nextTick(() => {
        this.searchFilter = []
      })
    }
    this.chartHeight = this.isSearchActive ? 150 : 180
  }

  private onSearchChange(event: any) {
    const type = event?.pointerType ?? 'none'
    if (
      type === 'mouse' ||
      (event.type === 'click' && !this.searchValue.trim())
    ) {
      // click 是点击清除按钮
      this.searchOptions()
    }
  }

  /**
   * 模糊搜索选项
   */
  private async searchOptions() {
    const searchValue = this.searchValue.trim()
    this.searchFilter = [
      [
        {
          col: this.column.name,
          filterType: 'like',
          values: [`%${searchValue}%`],
        },
      ],
    ]
    this.columnStat = await this.getColumnStat({
      ...this.columnStatParams,
      searchFilter: this.searchFilter,
      mode: 1,
      curPage: 1,
    })
    this.userSetStatMode = 'detail'
    this.chartConfig = this.getChartConfig(this.columnStat)
  }

  private singleClickTime = 200
  private timeOut: any = null

  private onDataColumnClick(event: MouseEvent) {
    clearTimeout(this.timeOut)
    const that = this
    this.timeOut = setTimeout(function () {
      that.$emit('click', event)
    }, this.singleClickTime)
  }

  private onDataColumnDBClick(event: Event) {
    event.stopPropagation()
    if (!this.isVisitor) {
      clearTimeout(this.timeOut)
      this.isEditingColumnName = this.renameEnabled
    }
  }

  private handleCreateNewColumn() {
    this.mode = 'dialog'
    const data = FILTER_COMPONENT_MAPPING[StatisticsAction.createColumn]
    this.configPanelType = data.componentName
    this.configPanelTitle = data.text
    this.filterParams = null
  }

  private handleFormulaNewColumn() {
    this.$emit('column-click')
  }

  private handlePartitionBy() {
    this.mode = 'dialog'
    const data = FILTER_COMPONENT_MAPPING[StatisticsAction.partitionBy]
    this.configPanelType = data.componentName
    this.configPanelTitle = data.text
    this.filterParams = null
  }

  private handleMenuItemClicked(type: string) {
    switch (type) {
      case StatisticsAction.renameColumn:
        this.isEditingColumnName = true
        break
      case StatisticsAction.copyColumn:
        this.copyColumn()
        break
      case StatisticsAction.keepOnlyColumn:
        this.keepOnlyColumn()
        break
      case StatisticsAction.deleteColumn:
        this.deleteColumn()
        break
      case StatisticsAction.trimSpace:
        this.trimSpace()
        break
      case 'binned':
      case 'detail':
        this.toggleStatMode(type)
        break
      case StatisticsAction.upperCase:
        this.upperCase()
        break
      case StatisticsAction.lowerCase:
        this.lowerCase()
        break
      case StatisticsAction.removeLetter:
        this.removeLetter()
        break
      case StatisticsAction.removeNumber:
        this.removeNumber()
        break
      case StatisticsAction.removePunctuation:
        this.removePunctuation()
        break
      case StatisticsAction.removeAllSpace:
        this.removeAllSpace()
        break
      case StatisticsAction.createColumn:
        this.createNewColumnOpt()
        break
      case StatisticsAction.mergeColumn:
        this.enableMergeColumn()
        break
      case StatisticsAction.chimeraIntoJson:
        this.openSpecifiedDialog(StatisticsAction.chimeraIntoJson)
        break
      case StatisticsAction.chimeraIntoArray:
        this.openSpecifiedDialog(StatisticsAction.chimeraIntoArray)
        break
      case StatisticsAction.editOrder:
        this.columnMenuEditOrder = true
        this.$nextTick(() => {
          this.openSpecifiedDialog(StatisticsAction.editOrder)
        })
        break
      case StatisticsAction.removeRepeatCells:
        this.removeRepeatCells()
        break
      default:
        break
    }
  }

  private createNewColumnOpt() {
    this.filterParams = {
      data: {
        ...this.createNewColumnFilter,
      },
    }
    this.onOk()
  }

  private async copyColumn() {
    let copiedCount = 0
    const regex = new RegExp(`^${this.column.name}_(\\d+)$`)
    this.columnNames.forEach((name) => {
      const suffix = +(regex.exec(name)?.[1] ?? '')
      copiedCount = Math.max(copiedCount, suffix)
    })

    this.filterParams = {
      data: {
        action: 'COPY',
        col: this.column.name,
        columnName: `${this.column.name}_${copiedCount + 1}`,
        description: `复制得到${this.column.name}_${copiedCount + 1}`,
        actionType: StatisticsAction.copyColumn,
      },
    }
    this.onOk()
  }

  private async keepOnlyColumn() {
    const cols =
      this.selectedColumns.length > 0
        ? this.selectedColumns.map((c) => c.name)
        : [this.column.name]
    this.filterParams = {
      data: {
        action: 'RETAIN',
        col: cols,
        retainCols: cols,
        description: `仅保留字段${cols}`,
        actionType: StatisticsAction.keepOnlyColumn,
      },
    }
    this.onOk()
  }

  private async deleteColumn() {
    const cols =
      this.selectedColumns.length > 0
        ? this.selectedColumns.map((c) => c.name)
        : [this.column.name]
    this.filterParams = {
      data: {
        action: 'REMOVE',
        col: cols,
        removeCols: cols,
        description: `删除字段${cols}`,
        actionType: StatisticsAction.deleteColumn,
      },
    }
    this.onOk()
  }

  private async trimSpace() {
    this.filterParams = {
      data: {
        action: 'PROCESS_TRIM',
        col: this.column.name,
        description: '裁剪空格',
        actionType: StatisticsAction.trimSpace,
      },
    }
    this.onOk()
  }

  private async toggleStatMode(mode: string) {
    if (this.statMode !== mode) {
      this.userSetStatMode = mode
      this.isSorting = false
      this.requestParams = {
        ...this.requestParams,
        sortType: 1,
        sortVal: 'ASC',
        curPage: 1,
      }
      if (this.$refs.columnSort) {
        // @ts-ignore
        this.$refs.columnSort.reset()
      }
      this.refreshColumnStat()
    }
  }

  private upperCase() {
    this.filterParams = {
      data: {
        action: 'PROCESS_UPPERCASE',
        col: this.column.name,
        description: '所有值已更改为大写',
        actionType: StatisticsAction.upperCase,
      },
    }
    this.onOk()
  }

  private lowerCase() {
    this.filterParams = {
      data: {
        action: 'PROCESS_LOWERCASE',
        col: this.column.name,
        description: '所有值已更改为小写',
        actionType: StatisticsAction.lowerCase,
      },
    }
    this.onOk()
  }

  private removeLetter() {
    this.filterParams = {
      data: {
        action: 'REMOVE_ALPHA',
        col: this.column.name,
        description: '从所有值中移除字母',
        actionType: StatisticsAction.removeLetter,
      },
    }
    this.onOk()
  }

  // 移除数字
  private removeNumber() {
    this.filterParams = {
      data: {
        action: 'REMOVE_DIGITAL',
        col: this.column.name,
        description: '从所有值中移除数字',
        actionType: StatisticsAction.removeNumber,
      },
    }
    this.onOk()
  }

  private removePunctuation() {
    this.filterParams = {
      data: {
        action: 'REMOVE_PUNCT',
        col: this.column.name,
        description: '从所有值中移除标点符号',
        actionType: StatisticsAction.removePunctuation,
      },
    }
    this.onOk()
  }

  private removeAllSpace() {
    this.filterParams = {
      data: {
        action: 'REMOVE_SPACE',
        col: this.column.name,
        description: '从所有值中移除所有空格',
        actionType: StatisticsAction.removeAllSpace,
      },
    }
    this.onOk()
  }

  private removeRepeatCells() {
    this.filterParams = {
      data: {
        action: 'TRANSFORM_REMOVE',
        actionType: StatisticsAction.removeRepeatCells,
        description: `去除所选列中的重复行:${this.column.name}`,
        partitionCols: [this.column.name],
        sortCol: '_record_id_',
        sortVal: 'DESC',
      },
    }
    this.onOk()
    console.log('removeRepeatCells')
  }

  /**
   * 转换列类型
   */
  private onColumnDataTypeClick(key: string) {
    if (this.isVisitor) {
      this.$message.error('暂无操作权限')
      return
    }
    this.filterParams = {
      data: {
        action: 'TYPE_TRANSFORM',
        semantic:
          this.selectedColumns.length > 1
            ? this.selectedColumns.map((c) => c.semantic)
            : [this.column.semantic],
        col: [
          ...new Set([
            ...this.selectedColumns.map((c) => c.name),
            this.column.name,
          ]),
        ], // this.selectedColumns.length > 0  this.column.name,
        toType: key,
        description: `转换为${(columnTypeMapping as any)[key]}类型`,
        actionType: StatisticsAction.typeTransform,
      },
    }
    this.onOk()
  }

  /**
   * 转换小数整数列格式
   */
  private handleFormatChange(type: string, digit: number) {
    if (this.isVisitor) {
      this.$message.error('暂无操作权限')
      return
    }
    this.filterParams = {
      data: {
        action: 'NUMBER_FORMAT_CONVERSION',
        col: this.column.name,
        subAction: type === 'percentage' ? '%' : 'E-NOTATION',
        description: type === 'percentage' ? '百分比' : '科学计数',
        actionType: StatisticsAction.dataFormatConversion,
        digit,
      },
    }
    this.onOk()
  }

  // private onColumnMenuClick() {
  //   const index = this.selectedColumns.findIndex(c => c.name === this.column.name)
  //   if (index === -1) {
  //     this.$emit('column-click')
  //   }
  // }

  /**
   * 根据指定的 action 打开对应的弹框
   * @param action 与 StatisticsAction 中的 action 对应
   * 目前正在使用的有： 嵌合成对象， 嵌合成数组， 编辑有序语义顺序
   */
  public openSpecifiedDialog(action: string) {
    this.mode = 'dialog'
    const data = FILTER_COMPONENT_MAPPING[action]
    this.configPanelType = data.componentName
    this.configPanelTitle = data.text
    this.filterParams = null
  }

  public columnMenuEditOrder: boolean = false // 编辑顺序是否是从 来自 menu

  /**
   * 确认/取消编辑顺序
   * @param info { flag: 'conform' / 'cancel', order?: []}
   */
  public confirmCancelOrder(info: any) {
    if (info.flag === 'confirm') {
      // 是 menu 编辑顺序产生操作, 或者是编辑操作历史 （不创建新列）
      if (this.columnMenuEditOrder || this.isEdit) {
        this.confirmChangeOrder(info.order)
      } else {
        // 修改语义新增列 （走新增语义， 创建新列）
        this.confirmChangeSemantic('order', this.CreateNewColumnName('order'), {
          order: info.order,
        })
      }
    } else {
      this.$emit('config-cancel')
    }
    this.toggleConfigPanel('view')
  }

  /**
   * menu修改当前列有序顺序 , 或者是历史历史列表编辑, 走此方法
   */
  public confirmChangeOrder(order: any[]) {
    const data: any = {
      action: 'EDIT_ORDER',
      col: this.column.name,
      order,
      description: '修改有序顺序',
      actionType: StatisticsAction.editOrder,
    }
    this.$emit('config-success', data) // 类似 this.onFilterParamsChange(data)， 不同的是传参方式
    this.columnMenuEditOrder = false
  }

  // 修改语义 有不符合规则的值 等信息
  public semanticTransformationConfigInfo: IChangeSemanticInfo | null = null

  /**
   * 修改语义， 部分行不符合， 弹框修改值， 新增列
   */
  public semanticTransitionCreateColumn(createColumnInfo: IChangeSemanticInfo) {
    this.semanticTransformationConfigInfo = createColumnInfo // 传递给 SemanticTransitionCreateColumnView.vue 用
    this.openSpecifiedDialog(StatisticsAction.semanticTransitionCreateColumn)
  }

  /**
   * 编辑好不符合规则的值， 新增列
   */
  public createNewSemanticColumn(editInfo: any) {
    const { columnName, update, toSemantic } = editInfo
    this.confirmChangeSemantic(toSemantic, columnName, { update })
  }

  /**
   * 重置参数
   */
  public resetParams() {
    this.requestParams = {
      curPage: 1,
      pageSize: 50,
      sortType: 1,
      sortVal: 'ASC',
    }
  }
}
</script>

<style lang="less" scoped>
.column-container {
  background: #fff;
  border-radius: 4px;
  box-shadow: 0 0 4px 0 rgba(88, 98, 230, 0.2);
  flex: 0 0 180px;
  margin: 0 3px;
  overflow: hidden;
  position: relative;

  &.selected {
    background: #fff;
    box-shadow: 0 0 0 1px #c5c9ff, 0 0 0 3px rgba(171, 176, 255, 0.1),
      0 0 4px 0 rgba(88, 98, 230, 0.2);
  }

  &.panel {
    display: flex;
    flex: 0 0 400px;
  }

  .view-content {
    display: inline-block;
    width: 180px;
  }

  .config-panel {
    border-left: 1px solid #e9e9e9;
    display: inline-block;
    height: 100%;
    padding: 24px 8px 8px;
    width: 220px;
  }

  .edit-header {
    align-items: center;
    background-color: #cdd0f7;
    display: flex;
    height: 24px;
    justify-content: space-between;
    left: 0;
    padding: 0 8px;
    position: absolute;
    top: 0;
    width: 400px;

    .btn-done {
      cursor: pointer;
    }

    .btn-cancel {
      position: absolute;
      right: 48px;
    }
  }

  .top-icons {
    height: 24px;
    padding: 6px 8px;

    .column-type-icon {
      float: left;
      height: 12px;

      .column-type-icon-trigger {
        color: #5b67e3;
        display: inline-block;
        max-width: 100px;
      }

      .arrow {
        color: #5b67e3;
        font-size: 10px;
        margin-left: 3px;

        &.menu-open {
          transform: rotate(180deg);
        }
      }
    }

    .applied-action-icon {
      border: 1px solid #5d637e;
      float: right;
      height: 12px;
      margin-left: 4px;
      width: 12px;
    }

    .attr-icon {
      float: right;
      font-size: 18px;
      margin-right: 2px;
    }

    .btns {
      color: #5d637e;
      display: none;
      float: right;
      margin-right: 2px;

      .icon {
        margin-left: 8px;
      }
    }
  }

  .name-and-btns {
    display: flex;
    flex-flow: row;
    flex-wrap: nowrap;
    height: 22px;
    padding: 0 8px;
    width: 100%;

    .column-name {
      color: #373b52;
      flex: 1;
      font-size: 14px;
      height: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      width: auto;
    }

    .unique-value-count {
      color: #222432;
      font-size: 12px;
      height: 100%;
      letter-spacing: 1.12px;
      margin-left: 10px;
      max-width: 54px;
      opacity: 0.5;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }

  .divider {
    background: #e9e9e9;
    height: 1px;
    margin: 1.5px 8px 0;
  }

  .stat-chart > .comp-chart {
    position: relative;
  }

  .mask-bar-container {
    padding: 9px 8px;

    .mask-bar {
      background: #e9e9e9;
      height: 20px;
      margin-top: 2px;
    }
  }

  &:hover {
    // .column-name {
    //   max-width: 60px;
    // }

    // .unique-value-count {
    //   max-width: 34px;
    // }

    .btns {
      display: block;
    }
  }

  .icon-container {
    cursor: pointer;
    font-size: 12px;
    line-height: 12px;
  }

  .stat-chart {
    position: relative;
  }

  .column-number {
    background: #fff;
    border-top: 1px solid #e9e9e9;
    bottom: 0;
    font-size: 14px;
    height: 32px;
    line-height: 32px;
    opacity: 0.95;
    position: absolute;
    text-align: center;
    width: 180px;
  }

  .search-box {
    display: flex;
    justify-content: center;
    padding-top: 5px;

    .search-input {
      width: 88%;
    }
  }

  .chart-options-input {
    font-size: 12px;
    height: 22px;
    left: 0;
    margin-left: 5px;
    position: absolute;
    top: 0;
    width: 165px;
    z-index: 900;
  }
}

.notAttribute {
  background-color: #fff;
  height: 100%;
  opacity: 0.5;
  position: absolute;
  top: 0;
  width: 100%;
}
</style>
